v.0.0.6
Some tools that might help with marking:
import io, sys, os
import nbformat as nb
import nbformat.v4.nbbase as nb4
This code will concatenate a set of notebooks in a specific folder into a Microsoft Word doc. You can choose to include or exclude the output of code cells.
The Word document will by defualt be saved as monolith.docx
.
#If apt-get can't find pandoc, you'll need to update the package listings first by uncommenting the update step
#!apt-get update
!apt-get install -y pandoc
#Show notebooks
!ls Part\ 02\ Notebooks/*.ipynb
#or in a subdir of the current dir such as "studentX":
#!ls studentX/*.ipynb
#ADD THE PATH TO THE NOTEBOOKS YOU WANT TO CONCATENATE HERE
#eg in subdir "studentX/notebooks", set: path='studentX/notebooks'
path='Part 02 Notebooks'
#OUTPUT declares whether you want the cell output or not; takes: True | False
OUTPUT=True
#The monolithic notebook is the content of several notebooks
monolith=nb4.new_notebook()
for fn in [f for f in os.listdir(path) if f.endswith('.ipynb')]:
fn='{}/{}'.format(path.rstrip('/'),fn)
tmpnb=nb.read(fn,nb.NO_CONVERT)
monolith.cells.append(nb4.new_markdown_cell('# NEW NOTEBOOK - {}'.format(fn)))
#For each cell in the original doc:
for i in tmpnb['cells']:
if (i['cell_type']=='markdown'):
monolith.cells.append(nb4.new_markdown_cell(i['source']))
elif (i['cell_type']=='code'):
#For the code cells, preserve any output text
cc=nb4.new_code_cell(i['source'])
if OUTPUT:
for o in i['outputs']:
cc['outputs'].append(o)
monolith.cells.append(cc)
#Save the monolithic notebook
nb.write(monolith,'monolith.ipynb')
#Convert it to markdown
!ipython nbconvert --to markdown monolith.ipynb
#Generate a Microsoft .docx file from the markdown
!pandoc -o monolith.docx -f markdown -t docx monolith.md
Script to add canned feedback.
Canned feedback can be one or more markdown and/or text cells in any combination.
Canned feedback must start with a markdown cell of the following form with a unique tag (eg XYZ1) for each separate piece of feedback:
MARKER FEEDBACK XYZ1
followed by the feedback cells. The canned feedback block ends with a markdown cell with a matching tag.
END MARKER FEEDBACK XYZ1
In the student notebook, add a markdown cell containing something of the form:
MARKER FEEDBACK XYZ1
The canned feedback to that tag will be inserted at that point in the student script, empinkened.
Other settings required:
Annotated student notebooks have the same name prefixed with the declared prefix value.
FEEDBACK_NOTEBOOK='Canned Feedback.ipynb'
PATH_TO_STUDENT_NOTEBOOKS='studentFolder'
#Prefix annotated files to show they are annotated; this may be an empty string: ''
ANNOTATED_FILE_PREFIX='annotated-'
STUDENT_OUTPUT=True
MARKER_OUTPUT=True
#If the EMERGENCY_BACKUP is true, if the annotated file prefix is the empty string,
# make sure we have a backup of original file
EMERGENCY_BACKUP=True
BACKUP_PREFIX='backup-'
def insertFeedback(n,tag):
fb=nb.read(FEEDBACK_NOTEBOOK,nb.NO_CONVERT)
intag=False
for i in fb['cells']:
if intag:
if (i['cell_type']=='markdown'):
if not i['source'].startswith('END MARKER FEEDBACK'):
cell=nb4.new_markdown_cell(i['source'])
cell.metadata.commentate=True
n.cells.append(cell)
elif (i['cell_type']=='code'):
#For the code cells, preserve any output text
cc=nb4.new_code_cell(i['source'])
cc.metadata.commentate=True
if MARKER_OUTPUT:
for o in i['outputs']:
cc['outputs'].append(o)
n.cells.append(cc)
if i['cell_type']=='markdown' and i['source'].split('END MARKER FEEDBACK')[-1].strip() == tag:
intag=False
break
elif i['cell_type']=='markdown' and i['source'].split('MARKER FEEDBACK')[-1].strip() == tag:
intag=True
return n
path=PATH_TO_STUDENT_NOTEBOOKS
#I've added a defensive measure to take a backup of a notebook if the annotated notebook prefix is an empty string
#Limit the files we're going to try to annotate to notebooks in the path that ARE NOT:
#- notebooks with an explict backup prefix
#- notebooks with an explict annotation prefix
for fn in [f for f in os.listdir(path) if f.endswith('.ipynb') and not f.startswith(BACKUP_PREFIX) and (ANNOTATED_FILE_PREFIX=='' or not f.startswith(ANNOTATED_FILE_PREFIX))]:
fna='{}{}'.format(ANNOTATED_FILE_PREFIX,fn)
annotated=nb4.new_notebook()
fn='{}/{}'.format(path.rstrip('/'),fn)
tmpnb=nb.read(fn,nb.NO_CONVERT)
#A defensive step to grab a backup of a notebook if the annotated notebook prefix is an empty string and backup does not exist
if EMERGENCY_BACKUP and ANNOTATED_FILE_PREFIX=='' and BACKUP_PREFIX.format(fna) not in os.listdir(path):
nb.write(tmpnb,'{}/{}{}'.format(path.rstrip('/'),BACKUP_PREFIX,fna))
#For each cell in the original doc:
#Should probably copy across cell metadata too?
for i in tmpnb['cells']:
if (i['cell_type']=='markdown'):
if i['source'].startswith('MARKER FEEDBACK'):
annotated=insertFeedback(annotated,i['source'].split('MARKER FEEDBACK')[-1].strip())
else:
annotated.cells.append(nb4.new_markdown_cell(i['source']))
elif (i['cell_type']=='code'):
#For the code cells, preserve any output text
cc=nb4.new_code_cell(i['source'])
if STUDENT_OUTPUT:
for o in i['outputs']:
cc['outputs'].append(o)
annotated.cells.append(cc)
nb.write(annotated,'{}/{}'.format(path.rstrip('/'),fna))
#A utility to help in testing...
!cp studentFolder/Test\ Script.js studentFolder/Test\ Script.ipynb